// (c) 2011 Greg Courville <Greg_Courville@GregLabs.com>

/* This file is part of GLExPIB.

   GLExPIB is free software: you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   GLExPIB is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with GLExPIB.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>
#include <stdarg.h>
#include <errno.h>
#include <string.h>
#include "glexpib.h"

#define RX_BUFFER_SIZE 4096
#define PORT_DEFAULT 0x378
#define ADDRESS_DEFAULT 0
#define TARGET_DEFAULT 1

int quiet = 0;

const struct option longOpts[] = {
	{"port", required_argument, 0, 'p'},
	{"maddr", required_argument, 0, 'a'},
	{"daddr", required_argument, 0, 'd'},
	{"quiet", no_argument, &quiet, 1},
	{"cmd", required_argument, 0, 'c'},
	{"query", required_argument, 0, 'q'},
	{0,0,0,0},
	};

void die(int err, const char *fmt, ...) {
	va_list args;
	va_start(args, fmt);
	fprintf(stderr, "Fatal: ");
	vfprintf(stderr, fmt, args);
	fprintf(stderr, "\n");
	va_end(args);
	exit(err);
	}

char *strDupe(const char *src) {
	size_t len = strlen(src);
	if(!len)
		return NULL;
	void *foo = malloc(len + 2);
	if(!foo)
		die(ENOMEM, "Out of memory?!?!");
	strncpy((char *)foo, src, len);
	((char *)foo)[len] = '\n';
	((char *)foo)[len+1] = 0;
	return (char *)foo;
	}

void info(const char *fmt, ...) {
	if(!quiet) {
		va_list args;
		va_start(args, fmt);
		vfprintf(stderr, fmt, args);
		fprintf(stderr, "\n");
		va_end(args);
		}
	}

void badArg(int optIndex, const char *optArg, const char *wanted) {
	die(EINVAL, "Invalid argument '%s' to option '%s': requires %s", optArg, longOpts[optIndex].name, wanted);
	}

int main(int argc, char **argv) {
	uint16_t port = PORT_DEFAULT;
	uint8_t address = ADDRESS_DEFAULT;
	uint8_t target = TARGET_DEFAULT;
	char *query = NULL;
	int getResponse = 0;
	int optIndex = 0, c;
	//TODO: handle quiet flag
	while((c = getopt_long(argc, argv, "", longOpts, &optIndex)) != -1)
		switch(c) {
			case 'p':
				if(sscanf(optarg, "%hx", &port) != 1)
					badArg(optIndex, optarg, "a hexidecimal integer no greater than 0x3FF");
				break;
			case 'a':
				if(sscanf(optarg, "%hhd", &address) != 1)
					badArg(optIndex, optarg, "a decimal integer in range [0,30]");
				break;
			case 'd':
				if(sscanf(optarg, "%hhd", &target) != 1)
					badArg(optIndex, optarg, "a decimal integer in range [0,30]");
				break;
			case 'q':
				getResponse = 1;
			case 'c':
				query = strDupe(optarg);
				break;
			default:
				continue;
			}
	int status = 0;
	do {
		info("Initializing bus...");
		status = glexpib_init(port, address);
		if(status)
			break;
		if(query) {
			info("Sending command...");
			status = glexpib_putStr(target, query, strlen(query));
			free(query);
			if(status)
				break;
			}
		if(getResponse) {
			info("Reading response...");
			size_t len = 0;
			char *buf = malloc(RX_BUFFER_SIZE);
			if(!buf)
				die(ENOMEM, "Out of memory?!?!");
			status = glexpib_getStr(target, buf, &len, RX_BUFFER_SIZE - 1);
			if(status)
				break;
			buf[len] = 0;
			printf("%s",buf);
			free(buf);
			}
		info("Releasing bus.");
		glexpib_quit(port, address);
		} while(0);
	switch(status) {
		case 0:
			break;
		case LPTGPIB_ERR_PERM:
			die(EPERM, glexpib_strerror(status));
		default:
			die(EINVAL, glexpib_strerror(status));
		}
	}
